home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / applic / backup / 8mmbackup.shar / 8mmbackup next >
Encoding:
Text File  |  1989-11-19  |  29.6 KB  |  874 lines

  1. #!/bin/csh
  2.  
  3. set version=1.5
  4.  
  5. # Copyright (C) Kenneth L. Manheimer 1988, 1989
  6. # National Institute of Standards and Technology, Gaithersberg, MD 20899
  7. # klm@cme.nist.gov or ..!uunet!cme-durer!klm     (301) 975-3539
  8.  
  9. # This script is distributed in the hope that it will be useful, but
  10. # WITHOUT ANY WARRANTY.  Neither the National Institute of Standards
  11. # and Technology, nor any author or distributor, accepts responsibility
  12. # to anyone for the consequences of using it or for whether it serves
  13. # any particular purpose or works at all, unless he says so in writing.
  14.  
  15. # Call by:
  16. #
  17. # 8mmbackup [ verify ]
  18. # or
  19. # fullPrimer [ inhibit ]
  20. #
  21. # Where 'verify' means just do preliminary variable assignments and fundamental
  22. # configuration verification (to validate presence of script, accounting,
  23. # subject directories, etc).  On first-ever dump (ie, no 'seqIDfile' present)
  24. # both the contents and the registry are exhaustive, regardless of whether full
  25. # or incremental backup is indicated or 'exhaustiveRegistry' is configured.
  26. # Otherwise (and generally): incrementals are run unless 'fullPrimer'
  27. # has been invoked (in default mode) since last backup, in which case
  28. # a full backup will be performed.  See man page for more complete
  29. # instructions.
  30.  
  31.  
  32. # This script performs online backup of directory structures, implementing:
  33. # - both exhaustive ("full") and incremental ("incr") culling of directory
  34. #   hierarchies, or just preparatory verification of configuration ("verify")
  35. # - registry of those saved files that were changed since previous backup
  36. # - exclusion of specified directories (eg, tmp, spool, lost+found,...),
  37. # - exclusion of specified paths (eg, remote mounts, standard distribution,...)
  38. # NOTE that the method employed is oriented for very high capacity backup
  39. # medium (at our site, an Exabyte 8mm video-tape drive); in particular, it is
  40. # necessary that the medium is able to accomodate at least an entire full
  41. # backup fit on a single volume.
  42.  
  43. # Change History - moved to seperate file 'Changelog'
  44.  
  45.  
  46. #vvvvvvvvvvvvvvvvv User designated (configuration) variables vvvvvvvvvvvvvvvvvv
  47. # The best way to configure this script is to
  48. # (1) Adjust the setting of 'scriptDir' below to the correct directory, and
  49. # (2) Put a copy of this section ("User designated ...") of the script
  50. #     in the 'outboardConfig' file and use that copy to make all your
  51. #     configuration settings instead of this one.  Since the 8mm.config file is
  52. #     not included as part of the shar distribution, this has the advantage
  53. #     that you won't need recreate your configuration every time you implement
  54. #     a new release.  The disadvantage is that you will have to make sure to
  55. #     track the more infrequent configuration section changes when they occur.
  56.  
  57. # Set 'scriptDir' to the directory where the backup scripts (including
  58. # this one) reside:
  59. set scriptDir=/usr/local/lib/8mmbackup
  60.  
  61. # Set 'outboardConfig' to the name of the config file you want to use so you
  62. # don't have to reassert your configuration every time you get a new version of
  63. # the script...
  64. set outboardConfig=$scriptDir/8mm.config
  65.  
  66. # Setting debug causes the output device to be /dev/null (and tape
  67. # control processing to be inhibited) and the target directories to be
  68. # only the 8mmbackup directory (and subdirs).  Also, all logging goes to
  69. # terminal only (rather than both terminal and log file).  Giving debug the
  70. # value 'verbose' causes very verbose operation.
  71. #set debug=verbose
  72. #set debug
  73.  
  74. # Set 'subjPaths' to a list of directory hierarchies that backup should cover.
  75. # Wild carding can be used in 'subjPaths' and 'excludePaths'.
  76. # eg 'set subjPaths=(/)' or 'set subjPaths=(/lib /etc /util/news)', ...
  77. set subjPaths=(/ /usr/spool/{mail,mqueue})
  78.  
  79. # Set 'excludePaths' to specific rooted paths of directories whose contents are
  80. # to be excluded from backup; use this, for instance, to exclude specific file
  81. # systems that are preserved elsewhere (eg, nfs mounts) or parts of the
  82. # standard distribution you'd prefer to rebuild rather than restore...  It's
  83. # unnecessary to duplicate entries excluded by 'excludeDirs' (below).
  84. # NOTE that it will *not* work to try to try to include something in subjPaths
  85. #      that is contained within a directory structure excluded in excludePaths
  86. #      - *all* offspring in excludePaths hierarchies are excluded.
  87. # eg: 'set excludePaths=(/usr/man/cat* /home/{norman,squire} /depot)
  88. set excludePaths=(/usr/man/cat*)
  89.  
  90. # Set 'excludeDirs' to pathless directory names whose contents are to be
  91. # excluded from preservation.  Csh wild card chars will work (see 'find' man
  92. # page), but be certain to escape them for the shell.
  93. # We use: 'set excludeDirs=(swap tmp spool)'
  94. set excludeDirs=(swap tmp newsgroups spool)
  95.  
  96. # BEING RETIRED
  97. ## Set 'exhaustiveRegistry' if you want the registry to contain the
  98. ## names of *all* the files that have been preserved, not just those
  99. ## that were modified since the previous backup.  (Otherwise the names
  100. ## of those files that have not changed since the prior backup will not
  101. ## be registered, though they are, of course, still preserved during a
  102. ## full backup.)
  103. #set exhaustiveRegistry
  104.  
  105. # Set 'realDev' to the no-rewind device that should receive the backup.
  106. # Tape control will always be applied to 'realDev', but when debug is
  107. # set the archives will be sent to /dev/null.
  108. set realDev=/dev/nrst1
  109.  
  110. # Set totalCap to (conservative) total capacity (in bytes) a single volume
  111. # holds.  We use 2.2 billion (2200000000) for the exabyte drive.
  112. set totalCap=2200000000
  113.  
  114. # Set 'errorNotificeTo' to a username where a mail message should be sent
  115. # in case of backup glitch.  (We use an alias, 'backupmaster', which
  116. # translates to the concerned parties.) 
  117. set errorNoticeTo=backupmaster
  118.  
  119. # Set 'verifyTolerance' if you want a fairly stringent but time consuming
  120. # review of the archive file to be done after the archive is written to tape.
  121. # Assign an integer value that will be taken as a +/- tolerance between the
  122. # roster of files designated for archival and a Table-Of-Contents of the
  123. # archive written to tape.  The tolerance is necessary for discrepancies
  124. # between the roster for archival and the archive contents due to things
  125. # like files that were deleted after file-system-scan but before archive
  126. # creation.  The tolerance will expose insidious archive corruption like disk
  127. # read failures, which cause cpio to corrupt the archive without indication
  128. # during writing.  The default is a 2% tolerance (ie, # of entries within 2% of
  129. # basis); use a larger number only if you need to do a backup during
  130. # substantial file-system modifications.  Make 'verifyTolerance' unset
  131. # if you want to have a much less assured but much faster verification
  132. # of the backup archive.
  133. set verifyTolerance=2
  134. #^^^^^^^^^^^^^^^^^^ User designated (configuration) variables ^^^^^^^^^^^^^^^^^
  135.  
  136.  
  137.  
  138. #vvvvvvvvvvvvvvvvvvvvvvvvvvv Get outboard config  vvvvvvvvvvvvvvvvvvvvvvvvvvv
  139. if (-e $outboardConfig) then
  140.   source $outboardConfig
  141. endif
  142. #^^^^^^^^^^^^^^^^^^^^^^^^^^^ Got outboard config ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  143.  
  144.  
  145.  
  146. #vvvvvvvvvvvvvvvvvvvvvvvvvvvv Contrived variables vvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  147. # (... and logging and error aliases)
  148.  
  149. # Script name -
  150. if ($?0) then
  151.   set scNm=$0; set scNm=$scNm:t
  152. else
  153.   set scNm=BackupScript
  154. endif
  155.  
  156. # dateStamp - date of backup, reset just before interlock claim below:
  157. set dateStamp="`date`"
  158.  
  159. # guard alias - set onintr label and guardState for guardedExit (below)
  160. alias guard "set guardState='\!*'; onintr \!*"
  161. guard ground
  162.  
  163. # guardedExit alias - exit with value through current guard
  164. alias guardedExit "set exitVal=\!*; goto" '$guardState'
  165. set exitVal=1
  166.  
  167. # interactive - 1 if running from a terminal, 0 elsewise
  168. set interactive=1; tty -s; if ($status == 1) set interactive=0
  169.  
  170. # acctDir - directory where accounting for backup processing is maintained:
  171. set acctDir=$scriptDir/accounting
  172.  
  173. # interlock - file present when backup already in progress.
  174. set interlock=$acctDir/interlock
  175.  
  176. # fullTrigger - file whose presence triggers full backup
  177. set fullTrigger=$acctDir/fullTrigger
  178.  
  179. # log - file where conduct and errors of backup session is logged
  180. set logfile=$acctDir/backup.log
  181.  
  182. # toLog alias - command used for logging messages:
  183. alias tolog "(\!*) |& tee -a $logfile"
  184.  
  185. # incrClause - find parameter which indicates file, with mod-time of last
  186. #           backup, to respect for incremental preserves:
  187. set incrClause=""
  188.  
  189. # pruneClause - 'excludeDirs' massaged (below) for use in find statement:
  190. set pruneClause=""
  191.  
  192. # awkFilterScript - file where (file-name/capacity filter) awk script resides:
  193. set awkFilterScript=$scriptDir/fnFilter.awk
  194.  
  195. # fgrepClause - 'excludePaths' massaged (below) for expression as fgrep filter;
  196. #          wild card globbing and symbolic links are expanded to
  197. #          their unravelled equivalents. 
  198. set fgrepClause=""
  199.  
  200. # fgrepClauseFile - fgrep clause residence (instead of passing thru cmd line)
  201. set fgrepClauseFile=$acctDir/fgrep.clause
  202.  
  203. # excludePathFilter - like the name says...
  204. alias excludePathsFilter "fgrep -vf $fgrepClauseFile"
  205.  
  206. # seqID - Sequence number identifying current backup *and* tape index.
  207. #      Each full backup re-initiates to seqID 0, so new tape must be used.:
  208. set seqID=""
  209.  
  210. # seqIDFile - File containing identifying sequence number of previous backup.  
  211. #          '0' for full backup.
  212. set seqIDFile=$acctDir/seqIDFile
  213.  
  214. # newSeqIDFile - The seqID for backup currently in progress:
  215. set newSeqIDFile=$seqIDFile.current
  216.  
  217. # seqStatsFile - table translating sequence registry IDs to actual dates:
  218. # format: seqID tape-capacity-consumed toDev mode (subjPaths) (excludePaths) \ 
  219. #      (excludeDirs) date
  220. set seqStatsFile=$acctDir/seqStats
  221.  
  222. # priorStatsFile - seqStats of prior cycle, exists only during full backup, for
  223. #           similar purpose as that of priorRegistry (below)
  224. set priorStatsFile=$acctDir/seqStats.prior
  225.  
  226. # capUsed - total amount of tape used so far (obtained from 'seqStats'):
  227. set capUsed=""
  228.  
  229. # capPending - amt bytes pending to go onto tape this dump:
  230. set capPending=""
  231.  
  232. # pends - working file where roster of files that have changed since previous
  233. #      backup is registered.  (This will always consist of only those files
  234. #      that have changed since last backup, even when additional files (ie,
  235. #      during full backup) are being preserved.):
  236. #    format: '-ls' format of find
  237. set pends=$acctDir/pends
  238.  
  239. # fullPends - working roster of files pending full dump (not just changed ones)
  240. set fullPends=$acctDir/fullPends
  241.  
  242. # registry - Current cycle's cumulative file of filenames whose contents were
  243. #         preserved *due to having been modified since prior backup*.  Ie,
  244. #         even during full backup, only recently-modified files are
  245. #         registered, though any files under 'subjectsPath' are saved then.
  246. #
  247. #         Format of registry entry: <file-path> <seq-id> [<seq-id> ...]
  248. #
  249. #         where <seq-id> is is the 'seqID' of each previous backup (up to
  250. #         and including the most recent full backup) that preserved a
  251. #         version of <file-path>.
  252. #
  253. #         This file is saved with subsequent cycle's full backup.  Thus each
  254. #         cycle's registry is available at beginning of subsequent cycle's
  255. #         tape.
  256. set registry=$acctDir/registry
  257.  
  258. #priorRegistry - exists only during full backup, so prior cycles registry will
  259. #         be saved with full backup.  Emplaced just prior to computing
  260. #         pends and removed after archival.
  261. set priorRegistry=$acctDir/registry.prior
  262.  
  263. # 'toDev' is where the archives are actually directed.  Normally it's
  264. # the same as $realDev, but during debug its just the /dev/null sink.
  265. if ($?debug == 1) then
  266.  if ("$debug" == "verbose") set echo
  267.  set toDev=/dev/null
  268.  set subjPaths=($scriptDir/accounting)
  269.  alias tolog '(\!*) |& cat'
  270. else
  271.   set toDev=$realDev
  272. endif
  273.  
  274. #8mmin/8mmout - filter interfaces to be used for access to 8mm tape device.
  275. alias 8mmin dd if='\!\!:1' ibs=5120
  276. alias 8mmout dd of='\!\!:1' obs=5120
  277. #^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Contrived variables ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  278.  
  279.  
  280.  
  281.  
  282. #vvvvvvvvvvvvvvvv Configuration verification and reformulation vvvvvvvvvvvvvvvv
  283. # For bozos that create silly aliases in their root accounts (:-)
  284. unalias rm
  285. unalias mv
  286. unalias cd
  287.  
  288. # Verify existence of 'scriptDir' and establish 'acctDir':
  289.  
  290. if (! -d $scriptDir) then
  291.   echo CONFIG ERROR: designated scriptDir \"$scriptDir\" does not exist
  292.   guardedExit 1                                # ====>
  293. else if (! -e $acctDir) then
  294.   mkdir $acctDir
  295.   touch $fullTrigger
  296. endif
  297.  
  298. # Establish 'logfile' if necessary:
  299. if (! -e $logfile) touch $logfile
  300.  
  301.  
  302.  
  303. #vvvvvvvvvvvvvvvvvvvvvvvvv backup vs priming branch vvvvvvvvvvvvvvvvvvvvvvvvvvv
  304. if ("$scNm" == "fullPrimer") then
  305.   if ("$1" == "") then
  306.     if (! -e $fullTrigger) then
  307.       touch $fullTrigger
  308.       tolog echo "${scNm}: Primed system for full backup `date`"
  309.     else
  310.       echo "${scNm}: System already primed for full backup `date`"
  311.     endif
  312.     echo  \'${0} inhibit\' to cancel
  313.   else if ("$1" == "inhibit") then
  314.     if (-e $fullTrigger) then
  315.       rm -f $fullTrigger
  316.       tolog echo "${scNm}: Primed system for incremental backup `date`"
  317.     else
  318.       echo "${scNm}: System already primed for incremental backup `date`"
  319.     endif
  320.     echo \'${0}\' to enable full backup
  321.   else
  322.     echo "Usage: ${scNm} [ inhibit ]"
  323.   endif
  324.   guardedExit 0                                # ====>
  325. endif
  326. #^^^^^^^^^^^^^^^^^^^^^^^^^ backup vs priming branch ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  327.  
  328.  
  329. #Hereon we're doing backup work:
  330.  
  331. #vvvvvvvvvvvvvvvvvvv Preliminary accounting and processing vvvvvvvvvvvvvvvvvvvv
  332. # mode - either 'full', 'incr', or 'verify'.
  333. #     'incr': default
  334. #     'full': if fullTrigger file exists
  335. #     'verify': with "verify" argument, do just preliminary stuff
  336. set mode=$1
  337. if ("$mode" != "verify") then
  338.   if (-e $fullTrigger) then
  339.     set mode='full'
  340.   else
  341.     set mode='incr'
  342.   endif
  343. endif
  344.  
  345. if ("$mode" != "full" && "$mode" != "incr" && "$mode" != "verify") then
  346.   if ($interactive == 1) then
  347.     echo Invalid mode: $mode
  348.     getMode:
  349.     echo -n "full, incr, verify, or quit? "
  350.     set mode="$<"
  351.     if ("$mode" != "full" && "$mode" != "incr" && "$mode" != "verify" \
  352.     && "$mode" != "quit") goto getMode
  353.     if ($mode == "quit") then
  354.       guardedExit 0                            # ====>
  355.     endif
  356.   else
  357.     tolog echo ${scNm} ERROR: "Usage: ${scNm} [ verify ]"
  358.     guardedExit 1                            # ====>
  359.   endif
  360. endif
  361.   
  362. # Verify absence of and claim interlock:
  363. if (-e $interlock) then
  364.   tolog echo ${scNm} INTERLOCK CONFLICT: \"$interlock\" interlock exists -
  365.   tolog echo another backup may already be in progress.
  366.   if ($interactive == 1) then
  367.     echo -n "Clear interlock and continue? [y or (default:) n] "
  368.     if ("$<" == "y") then
  369.       rm $interlock
  370.       goto interlockClear                        # vvvvv
  371.     else
  372.     endif
  373.   endif
  374.   tolog echo ${scNm} interlock collision - exiting
  375.   guardedExit 1                                # ====>
  376. endif
  377.  
  378. interlockClear:
  379.  
  380. set dateStamp=`date`
  381. echo $dateStamp > $interlock
  382. guard cleanupInterlock
  383.  
  384. # Begin log entry for this backup session:
  385. tolog echo -n "${scNm} v ${version}: $mode mode            "
  386. tolog echo $dateStamp
  387. tolog echo "Configuration Stage..."
  388. tolog echo -n "  Host: `hostname`"
  389. tolog echo -n "  Backup device: $realDev"
  390. if ($?debug == 1) then
  391.   tolog echo -n " (in debug, dump to $toDev)"
  392. endif
  393. tolog echo " - designated capacity $totalCap"
  394. tolog echo "  Subject paths: $subjPaths"
  395. tolog echo "  Exclude paths: $excludePaths"
  396. tolog echo "  Exclude dir basenames: $excludeDirs"
  397.  
  398. # Add accounting dir to head of subjPaths so accounting resides at head of
  399. # archive, is fast and easy to retrieve:
  400.  
  401. set subjPaths=($acctDir $subjPaths)
  402.  
  403. # Normalize directories:
  404.  
  405. # Filter paths to:
  406. # - reveal erroneous references
  407. # - normalize valid references to be "relative rooted" paths (paths prefixed
  408. #   with './') whose contents are to be excluded from preservation; using
  409. #   "relative rooted" paths with cpio will allow restoration of files, with
  410. #   all the information about their path locations, to arbitary places (ie,
  411. #   into the directory where resurrection is invoked).
  412. foreach curPathsVar (subjPaths excludePaths)
  413.   set accumPath=""
  414.   set pathExists=1
  415.   # use getval as a command to get value of current paths variable:
  416.   alias getval echo '$'$curPathsVar
  417.   foreach aPath (`getval`)
  418.     if (! -e $aPath) then
  419.       tolog echo ${scNm} CONFIG ERROR: no such file \"$aPath\" "($curPathsVar)"
  420.       set pathExists=0
  421.     else
  422.       set unraveled=`(cd $aPath; pwd)`
  423.       if ($status == 1) then                         # bad dir:
  424.    tolog echo ${scNm} CONFIG ERROR: \"$aPath\" not a directory "($curPathsVar)"
  425.     set pathExists=0
  426.       endif
  427.     endif
  428.     if ($pathExists == 0) then
  429.       switch ($curPathsVar)
  430.       case subjPaths:
  431.     tolog echo "  Nonexistant subject directory - drastic error, exiting."
  432.         guardedExit 1                            # ====>
  433.     breaksw
  434.       case excludePaths:
  435.     tolog echo "  Nonexistant exclude-directory - minor error, continuing."
  436.     set unraveled=""
  437.       endsw
  438.       set pathExists=1                        # reinit pathExists
  439.     else
  440.       set accumPath=($accumPath .$unraveled)
  441.     endif
  442.   end
  443.   set $curPathsVar=($accumPath)
  444. end
  445.  
  446. # Formulate fgrep clause:
  447. rm -f $fgrepClauseFile
  448. if ("$excludePaths" != "") then
  449.   :>! $fgrepClauseFile
  450.   foreach aPath ($excludePaths)
  451.     echo $aPath >> $fgrepClauseFile
  452.   end
  453. endif
  454.  
  455. if ($mode == "verify") then
  456.   tolog echo "  Verification complete."
  457.   guardedExit 0                                # ====>
  458. endif
  459. #^^^^^^^^^^^^^^^^^^^ Preliminary accounting and processing ^^^^^^^^^^^^^^^^^^^^
  460.  
  461.  
  462.  
  463. #vvvvvvvvvvvvvvvvvvvvvvvv Establish sequence info vvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  464. # Establish new seqID and file, and get prior capacity.
  465. # Do echo to 'newSeqIDFile' last so mod time is as near find time as possible.
  466. tolog echo -n "Preliminary Accounting Stage...                "
  467. tolog date +"%T %D"
  468. set seqID=0
  469. set capUsed=0
  470. if ($mode == "full") then
  471.   if (-e $registry) mv -f $registry $priorRegistry
  472.   if (-e $seqStatsFile) mv -f $seqStatsFile $priorStatsFile
  473.   touch $registry
  474.   touch $seqStatsFile
  475. else
  476.   if (-e $priorStatsFile || -e $priorRegistry) then
  477.     rm -f $priorStatsFile
  478.     rm -f $priorRegistry
  479.   endif
  480.   # Get prior capacity
  481.   if (! -z $seqStatsFile) then
  482.     set capUsed=`tail -1 $seqStatsFile | awk '{print $2}'`
  483.   endif
  484.   if (-e $seqIDFile && ! -z $seqIDFile) then
  485.     set seqID=`cat $seqIDFile`
  486.     @ seqID += 1
  487.   else
  488.     set seqID=0
  489.   endif
  490. endif
  491. guard cleanupSeqID
  492. tolog echo "  Current seq id: $seqID"
  493. echo $seqID >! $newSeqIDFile
  494. #^^^^^^^^^^^^^^^^^^^^^^^ Established sequence info ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  495.  
  496.  
  497.  
  498. #vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Get to root vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  499. cd /
  500. #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Got to root ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  501.  
  502.  
  503. #vvvvvvvvvvvvvvvvvvvvvvvvvvvv Position tape vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  504. tolog echo -n "Tape Positioning Stage...                "
  505. tolog date +"%T %D"
  506. if ($?debug != 1) then
  507.  
  508. set result=`mt -f $realDev rew`
  509. set exitVal=$status
  510. if ($exitVal != 0) then
  511.   tolog echo ${scNm} Error: while rewinding tape $realDev -
  512.   tolog echo "  mt (rew): $result (exit value $exitVal)"
  513.   tolog echo "  Aborting..."
  514.   guardedExit $exitVal
  515. else
  516.   set result=`mt -f $realDev fsf $seqID`
  517.   set exitVal=$status
  518.   if ($exitVal != 0) then
  519.     tolog echo ${scNm} Error: while positioning tape $realDev -
  520.     tolog echo "  mt (fsf $seqID): $result (exit value $exitVal)"
  521.     tolog echo "  Aborting..."
  522.     guardedExit $exitVal
  523.   endif
  524. endif
  525.  
  526. else
  527.   tolog echo "  Tape positioning would be done now if debug weren't set."
  528. endif
  529. #^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Tape positioned ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  530.  
  531.  
  532.  
  533.  
  534. #vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Compute rosters vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  535. # NOTE Roster and extent of changed files is always computed; that of entire
  536. # subject hierarchy is computed only for full backup, and then that extent
  537. # is the one we're concerned with (though the roster of changed files is the
  538. # one that's registered, providing the seed for a registry of file-mod dates).
  539.  
  540. tolog echo -n "Candidate-Computation Stage...                "
  541. tolog date +"%T %D"
  542.  
  543. # incrClause will make preserves respect the mod-time of seqIDFile unless
  544. # seqIDFile doesn't exist in which case no mod-time respective pruning is done:
  545. if (-e $seqIDFile) then
  546.   set incrClause="-newer $seqIDFile "
  547. endif
  548.  
  549. # Determine pruneClause from excludeDirs:
  550. foreach dir ( $excludeDirs )
  551.   if ("$pruneClause" == "") then
  552.     set pruneClause="-name $dir"
  553.   else
  554.     set pruneClause="$pruneClause -o -name $dir"
  555.   endif
  556. end
  557. if ("$pruneClause" != "") then
  558.   set pruneClause="( $pruneClause ) -prune"
  559. endif
  560.  
  561. # awkFilter will take the find '-ls' format and print the filenames into
  562. #        the filename argument, returning the total volume of the files.
  563. alias awkFilter awk -f $awkFilterScript argFile='\!*' -
  564.  
  565. if ("$excludePaths" != "") then
  566.   alias candidateFilter "excludePathsFilter | awkFilter"
  567. else
  568.   alias candidateFilter "awkFilter"
  569. endif
  570. rm -f $pends
  571. set incrPruneClause=""
  572. if ("$pruneClause" != "") then
  573.   set incrPruneClause="-a $pruneClause -o $pruneClause"
  574. endif
  575. set capPending=`find $subjPaths $incrClause -ls $incrPruneClause | candidateFilter $pends`
  576.  
  577. if ($mode == "full") then
  578.   rm -f $fullPends
  579.   set capPending=`find $subjPaths -ls $pruneClause |candidateFilter $fullPends`
  580. endif
  581. #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Computed rosters ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  582.  
  583.  
  584.  
  585.  
  586. #vvvvvvvvvvvvvvvvvvvv Verify sufficient capacity available vvvvvvvvvvvvvvvvvvvv
  587. tolog echo -n "  Capacity pending this dump: $capPending bytes"
  588. set totalPending=$capUsed
  589.  
  590. if (`echo "if ($totalCap < $capUsed + $capPending) 1" | bc` == "1") then
  591.   tolog echo " "
  592.   if ("$mode" == "full") then
  593.     tolog echo ${scNm} CAPACITY OVERFLOW: Major problem
  594.     tolog echo " full dump extent ($capPending) greater than designated $toDev"
  595.     tolog echo " capacity ($totalCap).  Distribute fs across multiple backups."
  596.     guardedExit 1
  597.   else
  598.     tolog echo ${scNm} CAPACITY OVERFLOW: Remaining tape capacity insufficient
  599.     tolog echo -n " for dump - total avail: $totalCap, prior used: $capUsed, "
  600.     tolog echo "now pending: $capPending.  Time to begin new tape."
  601.     guardedExit 1                            # ====V
  602.   endif
  603. else
  604.   set capUsed=`echo $capUsed + $capPending | bc`
  605.   if ("$mode" == "incr") then
  606.     tolog echo , brings total on tape to: $capUsed
  607.   else
  608.     tolog echo " "
  609.   endif
  610. endif
  611. #^^^^^^^^^^^^^^^^^^^^ Verify sufficient capacity available ^^^^^^^^^^^^^^^^^^^^
  612.  
  613.  
  614.  
  615. #vvvvvvvvvvvvvvvvvvvvvvvvvvv Archive files to tape vvvvvvvvvvvvvvvvvvvvvvvvvvvv
  616. tolog echo -n "File Archival Stage...                    "
  617. tolog date +"%T %D"
  618.  
  619. guard cleanupTapeOverflow
  620.  
  621. tolog echo -n "  "
  622.  
  623. doWrite:
  624. if ("$mode" == "incr") then
  625.   tolog (cpio -oB < $pends | 8mmout $toDev)
  626.   set exitVal=$status
  627. else
  628.   tolog (cpio -oB < $fullPends | 8mmout $toDev)
  629.   set exitVal=$status
  630. endif
  631.  
  632. # NOTE that exitVal here reflects only the disposition of the '>'
  633. # redirection (which happens to be what we're concerned with)
  634. if ($exitVal != 0) then
  635.   if (! $?retriedWrite) then
  636.     # This nauseating finagle is necessary to rewrite intermediate files on
  637.     # Exabyte drive (at the least for Perfect Byte st drivers under Sun OS4)
  638.     set retriedWrite; set exitVal=0
  639.     tolog echo "Hardening eof mark and retrying write..."
  640.     tolog echo -n "  "
  641.     # Position to where an extra eof mark will solve the problem:
  642.     mt -f $realDev rew; mt -f $realDev fsf $seqID; mt -f $realDev bsf 1
  643.     # ... and write it:
  644.     mt -f $realDev eof 1
  645.     # ... then reposition to correct spot for writing:
  646.     mt -f $realDev rew; mt -f $realDev fsf $seqID
  647.     # ... and retry just once more:
  648.     goto doWrite
  649.   else
  650.     tolog echo ${scNm}: "Error $exitVal writing on backup device $toDev - Aborting..."
  651.     guardedExit $exitVal
  652.   endif
  653. endif
  654. guard cleanupSeqID
  655. #^^^^^^^^^^^^^^^^^^^^^^^^^^^ Archived files to tape ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  656.  
  657.  
  658.  
  659. #vvvvvvvvvvvvvvvvvvvvvvvv Verify integrity of dump vvvvvvvvvvvvvvvvvvvvvvvvvv
  660. # Cursorily read dump file into /dev/null to verify its integrity
  661. tolog echo -n "Dump integrity verification stage...            "
  662. tolog date +"%T %D"
  663.  
  664. set result=`mt -f $realDev rew`
  665. set exitVal=$status
  666. if ($exitVal != 0) then
  667.   tolog echo ${scNm} Error: positioning tape $realDev during verification -
  668.   tolog echo "  mt (rew): $result (exit value $exitVal)"
  669.   tolog echo "  Aborting..."
  670.   guardedExit $exitVal
  671. else
  672.   set result=`mt -f $realDev fsf $seqID`
  673.   set exitVal=$status
  674.   if ($exitVal != 0) then
  675.     tolog echo ${scNm} Error: positioning tape $realDev during verification -
  676.     tolog echo "  mt (fsf $seqID): $result (exit value $exitVal)"
  677.     tolog echo "  Aborting..."
  678.     guardedExit $exitVal
  679.   endif
  680. endif
  681.  
  682. if ($?verifyTolerance) then
  683.   if ($mode == "full") then
  684.     set pendLines=`cat $fullPends | wc -l`
  685.   else
  686.     set pendLines=`cat $pends | wc -l`
  687.   endif
  688.   # Determine minimum and maximum toc lines within tolerance of pend lines:
  689.   set tol=$pendLines; @ tol /= 100; @ tol *= $verifyTolerance
  690.  
  691.   set tapeTOC=`8mmin $realDev | cpio -it | wc -l`
  692.  
  693.   if ($tapeTOC < $pendLines - $tol) then
  694.     tolog echo "${scNm} Error: archive table of contents too small -"
  695.     tolog echo " Roster had $pendLines entries, archive had $tapeTOC."
  696.     tolog echo " aborting..."
  697.     guardedExit 1
  698.   else if ($tapeTOC > $pendLines + $tol) then
  699.     tolog echo "${scNm} Error: archive table of contents excessively large -"
  700.     tolog echo " Roster had $pendLines entries, archive had $tapeTOC."
  701.     tolog echo " aborting..."
  702.     guardedExit 1
  703.   endif
  704. else    # do minimal verify:
  705.   tolog (8mmin $realDev > /dev/null)
  706.   set exitVal=$status
  707.   if ($exitVal != 0) then
  708.     tolog echo "${scNm} Error: $exitVal reading current dump - Aborting..."
  709.     guardedExit $exitVal
  710.   endif
  711. endif
  712. #^^^^^^^^^^^^^^^^^^^^^^^ Verified integrity of dump ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  713.  
  714.  
  715.  
  716. #vvvvvvvvvvvvvvvvvvvvvvvvvv Commit new accounting vvvvvvvvvvvvvvvvvvvvvvvvvvvv
  717. tolog echo -n "Accounting Commitment Stage...                "
  718. tolog date +"%T %D"
  719.  
  720. if ($mode == "full") then
  721.   if ($?debug != 1) then
  722.  
  723.     rm -f $registry
  724. #    # Either convert fullPends to registry or start with empty registry:
  725. #    if ($?exhaustiveRegistry == 1) then
  726.       #  strip leading './'s, snoc on <underscore>s, and sort fullPends:
  727.       sed -e "s-./\(.*\)-\1 .-" $fullPends | grep -v "^ " | sort -o $registry
  728. #    else
  729. #      touch $registry
  730. #    endif
  731.  
  732.   endif
  733.   rm -f $fullTrigger
  734. else
  735.   if (-e $fullPends) rm -f $fullPends
  736. endif
  737.  
  738. # Put seqid in pends - strip leading '.'s, snoc on seqID's, and sort:
  739. sed -e "s-./\(.*\)-\1 $seqID-" $pends | grep -v "^ " | sort -o $pends
  740.  
  741. # Merge $pends into $registry:
  742.  
  743. # Use awk instead of join to do the merge of existing registry with new pends.
  744. # NOTE - 'join' would be perfectly suitable in stead of 'awk' here but
  745. # it has a serious and relevant bug - ref file 'join.bug' in distribution.
  746. # full and incr mode require different merge procedures:
  747. if ($mode == "full") then
  748.   # full-mode-specific awk statement will reject repeated entries,
  749.   # leaving either a "." terminated entry if the file was not present
  750.   # in the incr pends, or else a "0" terminated entry if it was.
  751.   alias modeAwk awk "'"'BEGIN { previous = ""; prevEntry = "" } \\
  752.             $1 == prevEntry { previous = $0 } \\
  753.             $1 != prevEntry { if (NR != 1) print previous \\
  754.                      previous = $0; prevEntry = $1 } \\
  755.             END { if (NR != 1) print previous } ' "'" -
  756. else
  757.   # incr-mode-specific awk statement will merge in additional entries,
  758.   # creating new entries or adding new seqids to existing instances if any.
  759.   alias modeAwk awk "'"'BEGIN { previous = ""; prevMark = ""} \\
  760.             $1 == previous { if ($NF != prevMark) \\
  761.                        { printf " %s",$NF \\
  762.                          prevMark = $NF } } \\
  763.             $1 != previous { if (NR != 1) printf "\n" \\
  764.                      printf "%s", $0 \\
  765.                      previous = $1; prevMark = $NF } \\
  766.             END { if (NR != 1) printf "\n" } ' "'" -
  767. endif
  768.  
  769. # Enter new seqIDs in registry:
  770. cat $registry $pends | sort -b +0 -1 +1 -2n - | modeAwk >! ${registry}.tmp
  771.  
  772. # Commit new accounting to place:
  773. if ($?debug != 1) then
  774.   mv ${registry}.tmp $registry
  775.   rm -f $pends
  776.   # Commit new statistics: 
  777.   echo -n "$seqID $capUsed $toDev $mode" >> $seqStatsFile
  778.   if ($?verifyTolerance) then
  779.     echo -n " ${tapeTOC}~${pendLines}%${verifyTolerance}" >> $seqStatsFile
  780.   else
  781.     echo -n " cursory" >> $seqStatsFile
  782.   endif
  783.   echo -n " ($subjPaths)" >> $seqStatsFile
  784.   echo -n " ($excludePaths)" >> $seqStatsFile
  785.   echo -n " ($excludeDirs)" >> $seqStatsFile
  786.   echo    " $dateStamp" >> $seqStatsFile
  787.  
  788.   # Establish new seqID in place of previous one:
  789.   mv -f $seqIDFile{,.prev}
  790.   mv $newSeqIDFile $seqIDFile
  791. else
  792.   tolog echo In debug mode - not committing accounting.
  793. endif
  794.  
  795.  
  796. guard cleanupInterlock
  797. #^^^^^^^^^^^^^^^^^^^^^^^^^ Committed new accounting ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  798.  
  799.  
  800.  
  801. #vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Finish vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  802. rm $interlock
  803. guard ground
  804. tolog echo Successful Completion.
  805. tolog echo -n "  start: $dateStamp, finish: "
  806. tolog date
  807. guardedExit 0
  808. #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DONE ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  809.  
  810.  
  811.  
  812. #vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv guarded exits vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  813. cleanupTapeOverflow:
  814. guard cleanupSeqID
  815. #rm -f $pends
  816.  
  817. cleanupSeqID:
  818. guard cleanupInterlock
  819. rm -f $newSeqIDFile
  820.  
  821. cleanupInterlock:
  822. guard ground
  823. rm -f $interlock
  824.  
  825. ground:
  826. onintr
  827.  
  828. # Do notification of backup error if non-zero exitVal:
  829. if ($exitVal != 0) then
  830.   tolog echo "${scNm}: `date` - error notification ..."
  831.   set reply="y"
  832.   if ("$interactive" == 1) then
  833.     tolog echo "${scNm}: A non-zero exit has been encountered."
  834. readReply:
  835.     tolog echo -n "  Send mail notification (y or n)? "
  836.     set replyRead="$<"
  837.     switch ("$replyRead")
  838.       case "n":
  839.       case "N":
  840.     set reply="n"
  841.     breaksw
  842.       case "y":
  843.       case "Y":
  844.     breaksw
  845.       default:
  846.     tolog echo "Please respond with either 'y' or 'n':"
  847.     goto readReply
  848.     endsw
  849.     tolog echo $reply
  850.   endif
  851.   if ("$reply" != "n") then
  852.     tolog echo "  Sending mail notification..."
  853.     set hostNm=`hostname`
  854.     mail -s "Backup error on $hostNm" $errorNoticeTo << EONotice
  855. A $exitVal exit value has occurred in the course of an ${scNm} run on
  856. host $hostNm.
  857.  
  858. Details of the run can be found at the end of the backup log file,
  859. $logfile
  860.  
  861. The relevant entry is for time stamp $dateStamp.
  862. EONotice
  863.     tolog echo "... Bye."
  864.   else
  865.     tolog echo "No mail notification sent; bye."
  866.   endif
  867. endif
  868. if ("$scNm" != "fullPrimer") then
  869. tolog echo "=================================================================="
  870. endif
  871.  
  872. exit $exitVal                                # ====>
  873. #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ guarded exits ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  874.